package seatiger.tomcat.catalina.core;

import seatiger.tomcat.catalina.*;
import seatiger.tomcat.catalina.logger.SystemOutLogger;
import seatiger.tomcat.catalina.util.LifecycleSupport;
import seatiger.tomcat.util.StringManager;

import javax.naming.directory.DirContext;
import javax.servlet.ServletException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public abstract class ContainerBase implements Container,Lifecycle,Pipeline {

    private String name;

    protected Pipeline pipeline = new StandardPipeline(this);

    protected volatile boolean started = false;

    protected LifecycleSupport lifecycle = new LifecycleSupport(this);

    private List<ContainerListener> containerListeners = new ArrayList<>();

    protected Logger logger;

    protected Loader loader;

    private ClassLoader parentClassLoader;

    /**
     * 上下文对象
     */
    protected DirContext resources = null;

    /**
     * 初始化标志
     */
    protected boolean initialized=false;

    /**
     * 父容器
     */
    protected Container parent = null;
    private Map<String,Container> children = new ConcurrentHashMap<>();
    protected Map<String,Mapper> mappers = new ConcurrentHashMap();
    protected Manager manager = null;

    protected static StringManager sm = StringManager.getManager(Constants.PACKAGE_CORE);

    /**
     * 默认的映射器
     */
    protected String mapperClass = null;

    @Override
    public Pipeline getPipeline() {
        return pipeline;
    }

    protected Mapper mapper = null;

    @Override
    @Deprecated
    public void invoke(Request request, Response response) throws IOException, ServletException {
        pipeline.invoke(request,response);
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public void setName(String name) {
        this.name = name;
    }

    @Override
    public void setParent(Container container) {
        this.parent = container;
    }

    @Override
    public Container getParent() {
        return parent;
    }

    @Override
    public void addChild(Container child) {
        synchronized(children) {
            //保证孩子的唯一性
            if (children.get(child.getName()) != null){
                throw new IllegalArgumentException("addChild:  Child name '" +child.getName() +"' is not unique");
            }
            child.setParent(this);
            //如果父容器已经启动了，需要把这个孩子节点再启动一次
            if (started){
                try {
                    startComponents(child);
                } catch (LifecycleException e) {
                    log("ContainerBase.addChild: start: ", e);
                    throw new IllegalStateException("ContainerBase.addChild: start: " + e);
                }
            }
            children.put(child.getName(),child);
            fireContainerEvent(ADD_CHILD_EVENT, child);
        }
    }

    @Override
    public <T extends Container>  T findChild(String name) {
        return name == null ? null : (T) children.get(name);
    }

    @Override
    public Container[] findChildren() {
        return children.values().stream().toArray(Container[]::new);
    }

    @Override
    public void removeChild(Container child) {
        if (children.containsKey(child.getName())){
            Container remove = children.remove(child.getName());
            remove.setParent(null);
            fireContainerEvent(REMOVE_CHILD_EVENT,child);
        }
    }

    @Override
    public void addMapper(Mapper mapper) {
        String protocol = mapper.getProtocol();
        if (mappers.get(protocol) != null){
            //每个容器 不同的协议Mapper只能添加一个
            throw new IllegalArgumentException("addMapper:  Protocol '" + mapper.getProtocol() + "' is not unique");
        }
        mapper.setContainer(this);
        mappers.put(protocol,mapper);
        if (mappers.size() == 1) {
            this.mapper = mapper;
        }else {
            this.mapper = null;
        }
        fireContainerEvent(ADD_MAPPER_EVENT,mapper);
    }

    @Override
    public Mapper findMapper(String protocol) {
        if (mapper != null) {
            return mapper;
        }
        return mappers.get(protocol);
    }

    @Override
    public Mapper[] findMappers() {
        Collection<Mapper> values = mappers.values();
        return values.toArray(new Mapper[values.size()]);
    }

    @Override
    public void removeMapper(Mapper mapper) {
        mappers.remove(mapper.getProtocol());
        fireContainerEvent(REMOVE_MAPPER_EVENT,mapper);
    }

    @Override
    public Container map(Request request, boolean update) {
        String protocol = request.getRequest().getProtocol();
        Mapper mapper = findMapper(protocol);
        return mapper == null ? null : mapper.map(request,update);
    }

    @Override
    public void start() throws LifecycleException {
        if (started){
            throw new LifecycleException(sm.getString("containerBase.alreadyStarted",logName()));
        }
        if (!initialized) {
            init();
        }
        //为容器触发启动之前的事件
        lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);
        addDefaultMapper(this.mapperClass);
        started = true;
        startComponents(loader);
        startComponents(logger);
        //启动Mapper
        startComponents(findMappers());
        //启动子容器
        startComponents(findChildren());
        //启动管道
        startComponents(pipeline);
        //为容器触发启动中的事件
        lifecycle.fireLifecycleEvent(START_EVENT,null);
        startComponents(manager);
        //启动之后的事件
        lifecycle.fireLifecycleEvent(AFTER_START_EVENT,null);
    }

    public void init() {
        initialized=true;
    }


    protected void addDefaultMapper(String mapperClass) {
        if (mapperClass == null || mappers.size()>=1){
            return;
        }
        //设置默认加载器
        try {
            Class clazz = Class.forName(mapperClass);
            Mapper mapper = (Mapper) clazz.newInstance();
            mapper.setProtocol("HTTP/1.1");
            addMapper(mapper);
        } catch (Exception e) {
            log(sm.getString("containerBase.addDefaultMapper", mapperClass),
                    e);
        }
    }

    @Override
    public Valve getBasic() {
        return pipeline.getBasic();
    }

    @Override
    public void setBasic(Valve valve) {
        pipeline.setBasic(valve);
    }

    @Override
    public void addValve(Valve valve) {
        pipeline.addValve(valve);
        fireContainerEvent(ADD_VALVE_EVENT,valve);
    }

    @Override
    public void removeValve(Valve valve) {
        pipeline.removeValve(valve);
        fireContainerEvent(REMOVE_VALVE_EVENT,valve);
    }

    @Override
    public Valve[] getValves() {
        return pipeline.getValves();
    }

    @Override
    public void addLifecycleListener(LifecycleListener listener) {
        lifecycle.addLifecycleListener(listener);
    }

    @Override
    public List<LifecycleListener> findLifecycleListeners() {
        return lifecycle.findLifecycleListeners();
    }

    @Override
    public void removeLifecycleListener(LifecycleListener listener) {
        lifecycle.removeLifecycleListener(listener);
    }

    @Override
    public void addContainerListener(ContainerListener listener) {
        containerListeners.add(listener);
    }

    @Override
    public List<ContainerListener> findContainerListeners() {
        return containerListeners;
    }

    @Override
    public void removeContainerListener(ContainerListener listener) {
        containerListeners.remove(listener);
    }

    public void fireContainerEvent(String type, Object data) {
        ContainerEvent event = new ContainerEvent(this,type,data);
        containerListeners.forEach(containerListener -> containerListener.containerEvent(event));
    }


    @Override
    public Logger getLogger() {
        if (logger != null){
            return logger;
        }
        if (parent != null){
            return parent.getLogger();
        }
        this.logger = new SystemOutLogger();
        return logger;
    }

    @Override
    public void setLogger(Logger logger) {
        this.logger = logger;
        if (logger != null){
            logger.setContainer(this);
        }
        //如果整个容器已经start了,将新换上来的Logger start一次
        if (started){
            try {
                startComponents(logger);
            }catch (LifecycleException e){
                log("ContainerBase.setLogger: start: ", e);
            }
        }
    }

    /**
     * 记录错误日志
     * @param message
     * @param throwable
     */
    protected void log(String message, Throwable throwable) {
        Logger logger = getLogger();
        if (logger != null){
            logger.log(logName() + ": " + message,throwable);
        }else {
            System.out.println(logName() + ": " + message + ": " + throwable);
            throwable.printStackTrace(System.out);
        }
    }

    protected void log(String message) {
        Logger logger = getLogger();
        if (logger != null){
            logger.log(logName() + ": " + message);
        } else{
            System.out.println(logName() + ": " + message);
        }
    }

    protected String logName() {
        return this.getClass().getSimpleName() + "[" + getName() +"]";
    }

    @Override
    public Loader getLoader() {
        if (loader != null){
            return loader;
        }
        if (parent != null){
            return parent.getLoader();
        }
        return null;
    }

    @Override
    public void setLoader(Loader loader) {
        this.loader = loader;
        if (loader != null){
            loader.setContainer(this);
        }
        if (started && loader != null){
            try {
                startComponents(loader);
            }catch (LifecycleException e){
            }
        }
    }

    @Override
    public ClassLoader getParentClassLoader() {
        if (parentClassLoader != null){
            return parentClassLoader;
        }
        if (parent != null){
            return parent.getParentClassLoader();
        }
        return null;
    }

    @Override
    public void setParentClassLoader(ClassLoader parent) {
        this.parentClassLoader = parent;
    }

    @Override
    public DirContext getResources() {
        if (resources != null){
            return resources;
        }
        if (parent != null){
            return parent.getResources();
        }
        return null;
    }

    @Override
    public void setResources(DirContext resources) {
        this.resources = resources;
    }

    @Override
    public Manager getManager() {
        if (manager != null){
            return manager;
        }
        if (parent != null){
            return parent.getManager();
        }
        return null;
    }

    @Override
    public void setManager(Manager manager) {
        this.manager = manager;
        if (manager != null){
            manager.setContainer(this);
        }
        if (started && manager != null){
            try {
                startComponents(manager);
            } catch (LifecycleException e) {
            }
        }
    }

    @Override
    public void stop() throws LifecycleException {
        //已经启动过的组件才能停止
        if (!started){
            throw new LifecycleException(sm.getString("containerBase.notStarted",logName()));
        }
        //通知对应的事件监听器
        lifecycle.fireLifecycleEvent(BEFORE_STOP_EVENT,null);
        lifecycle.fireLifecycleEvent(STOP_EVENT, null);
        started = false;
        //停止对应的组件
        stopComponents(pipeline);
        stopComponents(findChildren());
        stopComponents(findMappers());
        stopComponents(resources);
        stopComponents(manager);
        stopComponents(logger);
        stopComponents(loader);
        lifecycle.fireLifecycleEvent(AFTER_STOP_EVENT, null);
    }

    @Override
    public Valve getFirst() {
        return pipeline.getFirst();
    }
}
